﻿/*
 * This file is part of quickReminder, a software for gamers
 *
 * Copyright (C) 2006-2009 Lucas Romero
 *
 * This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License along with this program; if not, see <http://www.gnu.org/licenses/>.
 */
using System;
using System.Collections.Generic;
using System.Threading;
using IoWorker;
using LucasCode.PublicTypes;

namespace LucasCode.Tools
{
    public class FileUploader
    {
        //public event EventHandler ProgressChanged;
        //public event EventHandler<UploadFile.ProgressChangedEventArgs> FileStatusChanged;
        public event EventHandler<UploadFile.ProgressChangedEventArgs> FileProgressChanged;
        public event EventHandler FileUploadComplete;
        //public event EventHandler UploadsComplete;

        #region Private Properties
        private long m_RunningUploads;
        private string m_Login;
        private int m_MaxConcurrentUploads = 2;
      
        private Queue<UploadFile> m_UploadQueue = new Queue<UploadFile>();
        private List<UploadFile> m_Uploads = new List<UploadFile>();
        #endregion

        #region Constructors
        #endregion

        #region Properties
        public long UploadsRunning
        {
            get { return Interlocked.Read(ref m_RunningUploads); }
        }
        
        public string LoginCookie
        {
            get { return m_Login; }
            set { m_Login = value; }
        }
        #endregion

        #region Methods
        private void StartUpload(object matchfileobj)
        {
            UploadFile matchFile = matchfileobj as UploadFile;

            if (string.IsNullOrEmpty(matchFile.MatchFile.Parent.Matchlink))
                throw new UploadException("Missing matchlink!");
            OnUploadStarted(matchFile);
            try
            {
                WebIO.UploadMatchFile(m_Login, matchFile, matchFile.MatchFile.Parent.Matchlink);
            }
            catch (UploadException ex)
            {
                matchFile.Status = ex.Message;
            }
            catch (Exception ex)
            {
                System.Diagnostics.Trace.TraceError(ex.ToString());
            }
            OnUploadFinished(matchFile);
        }

        public UploadFile AddMatchFileToQueue(MatchFile file)
        {
            System.Diagnostics.Trace.TraceInformation("Adding " + file.Filename + " to uploadqueue");
            UploadFile uploadFile = new UploadFile(file);
            lock (m_UploadQueue)
                m_UploadQueue.Enqueue(uploadFile);
            uploadFile.Status = IoHandler.IOStrings.Queued;
            return uploadFile;
        }

        public void StartUpload(string cookie, string matchlink)
        {
            System.Diagnostics.Trace.TraceInformation("Processing queue with " + m_UploadQueue.Count + " files");
            m_Login = cookie;

            ProcessQueue();
        }

        /*private void CancelUploads(UploadFile[] m_Files)
        {
            lock (m_UploadQueue)
                m_UploadQueue.Clear();
            foreach (UploadFile f in m_Files)
                f.Cancel();
        }*/

        public void CancelUploads()
        {
            lock(m_Uploads)
                lock (m_UploadQueue)
                {
                    m_UploadQueue.Clear();
                    foreach (UploadFile f in m_Uploads)
                    {
                        f.Cancel();
                    }
                    m_Uploads.Clear();
                }
        }

        private void ProcessQueue()
        {
            // be aware of the multithreaded nature of this uploading class
            // lock the queue because it could be used in multiple threads simultaneously
            // and read the number of running uploads with help of the Interlocked class
            // because it could also be modified from various threads at the same time
            lock (m_UploadQueue)
                lock (m_Uploads)
                    if (m_UploadQueue.Count > 0 && m_Uploads.Count < m_MaxConcurrentUploads)
                    {
                        UploadFile file = m_UploadQueue.Dequeue();
                        m_Uploads.Add(file);
                        file.Status = IoHandler.IOStrings.Pooled;
                        ThreadPool.QueueUserWorkItem(new WaitCallback(StartUpload), file);
                    }
        }
        #endregion

        #region EventHandler
        internal void OnUploadStarted(UploadFile sender)
        {
            Interlocked.Increment(ref m_RunningUploads);
            sender.ProgressChanged += new EventHandler<UploadFile.ProgressChangedEventArgs>(sender_ProgressChanged);
            sender.Status = IoHandler.IOStrings.Initializing;
        }

        void sender_ProgressChanged(object sender, UploadFile.ProgressChangedEventArgs e)
        {
            EventHandler<UploadFile.ProgressChangedEventArgs> h = FileProgressChanged;
            if (h != null)
                h(this, e);

        }
        void OnUploadFinished(UploadFile sender)
        {
            System.Diagnostics.Trace.TraceInformation("Upload finished for " + sender.MatchFile.Filename + " (match " + sender.MatchFile.Parent.Matchlink + ")");
            lock (m_Uploads)
                m_Uploads.Remove(sender);
            Interlocked.Decrement(ref m_RunningUploads);
            ProcessQueue();

            lock (m_Uploads)
            {
                lock (m_UploadQueue)
                {
                    if (m_UploadQueue.Count <= 0 && m_Uploads.Count <= 0)
                    {
                        EventHandler h = FileUploadComplete;

                        System.Diagnostics.Trace.TraceInformation("All files successfully uploaded");

                        if (h != null)
                            h(this, new EventArgs());
                    }
                }
            }
        }

        /*internal void OnFileStatusChanged(MatchFile sender)
        {
            // copying the event handler reference is good practice
            // because theoretically there is the chance that someone unsubscribes
            // from the event right between the if() and the calling of the event handler
            EventHandler<UploadFile.ProgressChangedEventArgs> h = FileStatusChanged;
            if (h != null)
                h(this, new UploadFile.ProgressChangedEventArgs(sender));
        }*/
        #endregion
    }
}
